export default class Deque {
  constructor({
    data = [],
    preLoad = 5,
    max = 20
  } = {}) {
    if (!Array.isArray(data)) throw new Error('初始队列必须为数组')
    this._list = data
    this.preLoad = Math.min(max, preLoad) // 前后预加载5个
    this.maxNum = Math.max(max, preLoad) // 最大存储数量
    this.curItemKey = null // 当前项唯一key值,常规类型为自身，对象取唯一id作为key
    this.getItemKey = this.defaultGetKey

    this.hasPrevLoading = false
    this.hasNextLoading = false

    this.hasPrevPreLoading = false
    this.hasNextPreLoading = false
  }

  defaultGetKey(e) {
    return e
  }

  setItemKey(itemToKey = this.defaultGetKey) {
    this.getItemKey = itemToKey
  }

  initList(list, curItemKey) {
    this._list = list
    this.curItemKey = curItemKey
  }

  init(item) {
    if (!item) return

    if (typeof item !== 'object') {
      this.curItemKey = item
    }
    else {
      this.curItemKey = this.getItemKey(item)
    }
    this._list.push(item)
  }

  clear() {
    this._list = []
    this.curItemKey = null // 当前项唯一key值,常规类型为自身，对象取唯一id作为key
    this.getItemKey = this.defaultGetKey

    this.hasPrevLoading = false
    this.hasNextLoading = false

    this.hasPrevPreLoading = false
    this.hasNextPreLoading = false
  }

  all() {
    return this._list
  }

  enqueue(item) { // 从结束端添加最新元素
    if (!item) return

    if (Array.isArray(item)) {
      this._list.push(...item)
      return
    }
    return this._list.push(item)
  }

  addFront(item) { // 从开始端添加元素
    if (!item) return

    if (Array.isArray(item)) {
      return this._list.unshift(...item)
    }
    return this._list.unshift(item)
  }

  dequeue() { // 从队列中删除前端元素
    return this._list.shift()
  }

  dequeueEnd() { // 从队列中删除后端元素
    return this._list.pop()
  }

  first() { // 第一个
    return this._list[0]
  }

  last() { // 最后一个
    return this._list[this._list.length - 1]
  }

  // getIndexByItem(item) {
  //     return this._list.findIndex(e => this.getItemKey(e) === this.getItemKey(item))
  // }

  getIndexByItemKey(key) {
    return this._list.findIndex(e => this.getItemKey(e) === key)
  }

  getCurInd() {
    return this.getIndexByItemKey(this.curItemKey)
  }

  async getPrev(api, filterParam, loadingCb) {
    if (this.hasPrevLoading) return { loading: true }

    const curInd = this.getIndexByItemKey(this.curItemKey)

    let reqPromise = null
    if (this._list[curInd + 1 - this.preLoad] === undefined) {
      // console.log('上一页预存数据<5条');
      const firstItem = this.getItemKey(this.first())

      const param = (filterParam && filterParam(firstItem)) || firstItem

      const apiFun = () => {
        return new Promise((resolve, reject) => {
          this.hasPrevPreLoading = true
          api(param).then(preList => {
            this.hasPrevPreLoading = false
            if (Array.isArray(preList) && preList.length > 0) {
              this.addFront(preList)
              while (this.size() > this.maxNum) {
                this.dequeueEnd()
              }
            }
            resolve(preList)
          }).catch(err => {
            this.hasPrevPreLoading = false
            reject(err)
          })
        })
      }
      reqPromise = apiFun()
    }

    const prev = this._list[curInd - 1]
    const prestore = this._list.slice(0, curInd - 1)

    if (prev === undefined) {
      // console.log('没有上一页');
      if (reqPromise) {
        this.hasPrevLoading = true
        loadingCb && loadingCb(true)
        try {
          await reqPromise

          this.hasPrevLoading = false
          loadingCb && loadingCb(false)

          const curInd = this.getIndexByItemKey(this.curItemKey)
          const prev = this._list[curInd - 1]

          if (prev)(this.curItemKey = this.getItemKey(prev))

          return {
            prev,
            prestore: this._list.slice(0, curInd - 1)
          }
        }
        catch (error) {
          this.hasPrevLoading = false
          loadingCb && loadingCb(false)

          return {
            prev,
            prestore: this._list.slice(0, curInd - 1)
          }
        }
      }
      else {
        return { prev, prestore }
      }
    }

    this.curItemKey = this.getItemKey(prev)

    return {
      prev,
      prestore
    }
  }

  async getNext(api, filterParam, loadingCb) {
    if (this.hasNextLoading) return { loading: true }

    const curInd = this.getIndexByItemKey(this.curItemKey)

    let reqPromise = null
    if (this._list[curInd + 1 + this.preLoad] === undefined) {
      // console.log('下一页预存数据<5条');
      const lastItem = this.getItemKey(this.last())
      const param = (filterParam && filterParam(lastItem)) || lastItem

      const apiFun = () => {
        return new Promise((resolve, reject) => {
          this.hasNextPreLoading = true
          api(param).then(preList => {
            this.hasNextPreLoading = false
            if (Array.isArray(preList) && preList.length > 0) {
              this.enqueue(preList)
              while (this.size() > this.maxNum) {
                this.dequeue()
              }
            }
            resolve(preList)
          }).catch(err => {
            this.hasNextPreLoading = false
            reject(err)
          })
        })
      }
      reqPromise = apiFun()
    }

    const next = this._list[curInd + 1]
    const prestore = this._list.slice(curInd + 1)

    if (next === undefined) {
      // console.log('没有下一页');
      if (reqPromise) {
        this.hasNextLoading = true
        loadingCb && loadingCb(true)
        try {
          await reqPromise

          this.hasNextLoading = false
          loadingCb && loadingCb(false)

          const curInd = this.getIndexByItemKey(this.curItemKey)
          const next = this._list[curInd + 1]

          if (next)(this.curItemKey = this.getItemKey(next))

          return {
            next,
            prestore: this._list.slice(0, curInd - 1)
          }
        }
        catch (error) {
          this.hasNextLoading = false
          loadingCb && loadingCb(false)

          return {
            next,
            prestore: this._list.slice(0, curInd - 1)
          }
        }
      }
      else {
        return { next, prestore }
      }
    }

    this.curItemKey = this.getItemKey(next)

    return {
      next,
      prestore
    }
  }

  size() {
    return this._list.length
  }
}
